package tech.waterism.utils;

import org.springframework.util.StringUtils;
import tech.waterism.constant.StringConstants;
import tech.waterism.entity.FMParameter;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;

public class CalibrationUtils {
    public static boolean isInequality(String exp) {
        return exp.contains("<") || exp.contains(">");
    }

    public static boolean isEquation(String exp) {
        String regx = "[^(=)]*(=){1}[^(=)]*";
        return exp.matches(regx) &&
                (exp.contains("+") || exp.contains("-") || exp.contains("*") || exp.contains("/")) &&
                !exp.contains("<") && !exp.contains(">");
    }

    public static boolean isValue(String exp) {
        String regx = "[^(=)]*(=){1}[^(=)]*";
        return exp.matches(regx) && !exp.contains("<") && !exp.contains(">") && !exp.contains("+") &&
                !exp.contains("-") && !exp.contains("*") && !exp.contains("/");
    }

    public static String[] seperateParam(String parameter) {
        return StringUtils.trimArrayElements(parameter.split("="));
    }

    /**
     * 根据取值范围设置参数值
     */
    public static String generateParam(String input) throws Exception {
        final Random random = new Random();
        String[] keyMap = CalibrationUtils.seperateParam(input);
        String[] values = StringUtils.trimAllWhitespace(keyMap[1]).split(StringConstants.COMMA);
        String value = null;
        switch (values.length) {
            case 1:
                value = values[0];
                break;
            case 2:
                double numberValue = Double.parseDouble(values[0]) +
                        (Double.parseDouble(values[1]) - Double.parseDouble(values[0])) * random.nextDouble();
                value = String.valueOf(numberValue);
                break;
            case 3:
                if (values[2].equalsIgnoreCase("N")) {
                    int numberValueInt = (int) Math.floor(Integer.parseInt(values[0]) +
                            (Integer.parseInt(values[1]) - Integer.parseInt(values[0])) * random.nextDouble());
                    value = String.valueOf(numberValueInt);
                } else {
                    throw new Exception("参数范围设置有误");
                }
                break;
            default:
                throw new Exception("参数范围设置有误");
        }
        return keyMap[0] + "=" + value;
    }

    public static void generateParamEquation(Map<String, String> map, String relation) throws Exception {
        String[] arr = StringUtils.trimAllWhitespace(relation).split("=|\\+|-");
        if (arr.length == 3) {
            if (relation.contains("-")) {
                if (map.containsKey(arr[0]) && map.containsKey(arr[1])) {
                    map.put(arr[1], arr[1] + "=" +
                            (Double.parseDouble(CalibrationUtils.seperateParam(map.get(arr[0]))[1]) -
                                    Double.parseDouble(arr[2])));
                } else {
                    throw new Exception("约束条件格式不正确");
                }
            } else if (relation.contains("+")) {
                if (map.containsKey(arr[0]) && map.containsKey(arr[1])) {
                    map.put(arr[0], arr[0] + "=" + (Double.parseDouble(arr[2]) -
                            Double.parseDouble(CalibrationUtils.seperateParam(map.get(arr[1]))[1])));
                } else {
                    throw new Exception("约束条件格式不正确");
                }
            }
        } else {
            throw new Exception("约束条件格式不正确");
        }
    }

    public static List<FMParameter> mapToParams(Map<String, String> paraMap) {
        List<FMParameter> result = new LinkedList<>();
        FMParameter parameter;
        for (String key : paraMap.keySet()) {
            parameter = new FMParameter();
            parameter.setParameter(paraMap.get(key));
            result.add(parameter);
        }
        return result;
    }

    public static double calPunishment(Map<String, Double> map, String relation) throws Exception {
        double result = 0;
        String[] arr = relation.trim().split("<|>|\\+|-");
        if (arr.length == 3 && map.containsKey(arr[0]) && map.containsKey(arr[1])) {
            if (relation.contains("<")) {
                if (relation.contains("+")) {
                    if (map.get(arr[0]) + map.get(arr[1]) > Double.parseDouble(arr[2])) {
                        result = 1000;
                    }
                } else if (relation.contains("-")) {
                    if (map.get(arr[0]) - map.get(arr[1]) > Double.parseDouble(arr[2])) {
                        result = 1000;
                    }
                }
            } else if (relation.contains(">")) {
                if (relation.contains("+")) {
                    if (map.get(arr[0]) + map.get(arr[1]) < Double.parseDouble(arr[2])) {
                        result = 1000;
                    }
                } else if (relation.contains("-")) {
                    if (map.get(arr[0]) - map.get(arr[1]) < Double.parseDouble(arr[2])) {
                        result = 1000;
                    }
                }
            }
        } else {
            throw new Exception("约束条件格式不正确");
        }
        return result;
    }

    /**
     * 运算符号数量验证
     */
    public static boolean countSymbol(String exp) {
        int calCnt = 0;
        int eqCnt = 0;
        for (int i = 0; i < exp.length(); i++) {
            char item = exp.charAt(i);
            //  61 =
            //  60 <
            //  62 >
            //  42 *
            //  43 +
            //  45 -
            //  47 /
            if (item == 60 || item == 61 || item == 62) {
                eqCnt++;
            } else if (item == 42 || item == 43 || item == 45 || item == 47) {
                calCnt++;
            }
        }
        return calCnt <= 1 && eqCnt <= 1;
    }

    public static void revertNashCoeff(double[] fitness, String[] objectives) {
        for (int i = 0; i < objectives.length; i++) {
            if (objectives[i].equals(StringConstants.COEFFICIENT_OF_DETERMINATION) ||
                    objectives[i].equals(StringConstants.COEFFICIENT_OF_DETERMINATION_SINGLE)) {
                fitness[i] = 1 - fitness[i];
            }
        }
    }

    public static void revertNashCoeff(Map<String, Double> objective, String[] array) {
        if (objective.containsKey(StringConstants.COEFFICIENT_OF_DETERMINATION)) {
            Double item = objective.get(StringConstants.COEFFICIENT_OF_DETERMINATION);
            double value = 1 - item;
            objective.put(StringConstants.COEFFICIENT_OF_DETERMINATION, value);
        }
        if (objective.containsKey(StringConstants.COEFFICIENT_OF_DETERMINATION_SINGLE)) {
            Double item = objective.get(StringConstants.COEFFICIENT_OF_DETERMINATION_SINGLE);
            double value = 1 - item;
            objective.put(StringConstants.COEFFICIENT_OF_DETERMINATION_SINGLE, value);
        }
    }
}
